package com.zyf.project.binaryproto.converter.impl.dynamic;

import com.zyf.project.binaryproto.converter.impl.AbstractDynamicValueConverter;
import com.zyf.project.binaryproto.exception.DataContractException;
import utils.binary.BytesSerializable;
import utils.binary.impl.BytesSlice;
import utils.binary.BytesUtils;
import utils.binary.io.BytesOutputBuffer;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

/**
 * @author Zhou Yifan
 */
public class BytesSerializableValueConverter extends AbstractDynamicValueConverter {

    private Constructor<?> constructor;

    public BytesSerializableValueConverter(Class<?> valueType) {
        super(valueType);
        if (!BytesSerializable.class.isAssignableFrom(valueType)) {
            throw new IllegalArgumentException("The specified type can't be assigned as BytesSerializable!");
        }
        // 检查是否存在以 byte[] 为参数的构造器；
        try {
            constructor = valueType.getConstructor(byte[].class);
            constructor.setAccessible(true);
        } catch (NoSuchMethodException e) {
            throw new DataContractException("No constructor with byte's array argument! --" + e.getMessage(), e);
        } catch (SecurityException e) {
            throw new DataContractException(e.getMessage(), e);
        }
    }

    /**
     * 写入一个动态长度的值，以一个头部的长度字节开始
     *
     * @param value  待写入的值
     * @param buffer 目标缓存
     * @return 写入的字节数
     */
    @Override
    public int encode(Object value, BytesOutputBuffer buffer) {
        byte[] bytes = value == null ? BytesUtils.EMPTY_BYTES : ((BytesSerializable) value).toBytes();
        int size = bytes.length;
        size += writeSize(size, buffer);
        buffer.write(bytes);
        return size;
    }

    /**
     * 数据片解码
     *
     * @param dataSlice 数据片
     * @return 解码后的对象
     */
    @Override
    public Object decode(BytesSlice dataSlice) {
        if (valueType == BytesSlice.class) {
            return dataSlice;
        }
        if (dataSlice.getSize() == 0) {
            return null;
        }
        byte[] bytes = dataSlice.getBytesCopy();
        try {
            return constructor.newInstance(bytes);
        } catch (InstantiationException
                 | IllegalAccessException
                 | IllegalArgumentException
                 | InvocationTargetException e) {
            throw new DataContractException(e.getMessage(), e);
        }
    }
}
